package net.boyandi.picback;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.swing.BoxLayout;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

import net.boyandi.picback.utils.Files;

import org.apache.log4j.Logger;

import at.fhj.utils.misc.ProgressTracker;
import at.fhj.utils.swing.JMsgPane;
import at.fhj.utils.swing.ProgressWorker;

/**
 * @author Ilya Boyandin
 */
public class Backuper extends ProgressWorker {

    private static Logger logger = Logger.getLogger(Backuper.class.getName());

    private final PicBack app;
    
    private final boolean removeNonStarred;
    private final boolean overwriteOlder;

    private int numCopied;
    private int numOverwritten;
    private int numNotOverwritten;
    private int numRemoved;
    private long sizeCopied;
    private long sizeRemoved;


    public Backuper(PicBack app, ProgressTracker progress, boolean removeNonStarred, boolean overwriteOlder) {
        super(progress);
        this.app = app;
        this.removeNonStarred = removeNonStarred;
        this.overwriteOlder = overwriteOlder;
    }

    @Override
    public Object construct() {
        try {
            startBackup();
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    showStats();
                }
            });
        } catch (final Throwable th) {
            JMsgPane.showErrorDialog(app, th);
            logger.error("Backup failed", th);
        }
        
        
        return null;
    }
    
    private void showStats() {
        JPanel panel = new JPanel();
        panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
        panel.add(new JLabel(
                "<html>" +
                "<table>" +
                    "<tr><th colspan=2>Backup Stats</td></tr>" +
                    "<tr><td>Backup status: </td><td align=center>" + 
                        (getProgressTracker().isFinished() ? "finished" : "cancelled") + "</td></tr>" +
                    "<tr><td colspan=2><hr></tr>" +
                    "<tr><td>Files copied: </td>" +
                        "<td align=right>" + numCopied + " (" + Files.humanReadableSize(sizeCopied) + ")</td></tr>" +
//                    "<tr><td colspan=2><hr></tr>" +
                    "<tr><td>Not modifed: </td>" +
                        "<td align=right>" + numNotOverwritten + "</td></tr>" +
//                    "<tr><td colspan=2><font size=-2>Since the last backup</font></td></tr>" +
                    "<tr><td colspan=2><hr></tr>" +
                    "<tr><td>Files overwritten: </td><td align=right>" + numOverwritten + "</td></tr>" +
                    "<tr><td>Files removed: </td>" +
                        "<td align=right>" + numRemoved + " (" + Files.humanReadableSize(sizeRemoved) + ")</td></tr>" +
                 "</table>"
        ));
        JOptionPane.showMessageDialog(app, panel, "Backup totals", JOptionPane.INFORMATION_MESSAGE);
    }

    public int getNumCopied() {
        return numCopied;
    }

    public int getNumOverwritten() {
        return numOverwritten;
    }

    public int getNumNotOverwritten() {
        return numNotOverwritten;
    }

    public int getNumRemoved() {
        return numRemoved;
    }

    public long getSizeCopied() {
        return sizeCopied;
    }

    public long getSizeRemoved() {
        return sizeRemoved;
    }
    
    private Object startBackup() throws IOException {
        numCopied = numOverwritten = numRemoved = numNotOverwritten = 0;
        sizeCopied = sizeRemoved = 0;
        
        logger.info("Backup started");
        ProgressTracker tracker = getProgressTracker();

        List<FolderScanResult> folders = app.getSelectedFolders();
        
        long totalBackupSize = 0;
        for (FolderScanResult f : folders) {
            totalBackupSize += f.getFullSize(); 
        }
        
        List<File> nonStarredToRemove = null;
        if (removeNonStarred) {
            nonStarredToRemove = new ArrayList<File>();
        }
        for (FolderScanResult f : folders) {
            if (tracker.isCancelled()) return null;

            String destFolder = Files.concatPathParts(
                    app.getSettings().getBackupDestinationFolder(), 
                    new File(f.getScanStartFolder()).getName(),
                    f.getRelativePath()
            );

            
            PicasaIniParser iniParser = null;
            String path = f.getPath();
            final boolean managedByPicasa = PicasaIniParser.isManagedByPicasa(path);
            if (removeNonStarred) {
                if (managedByPicasa) {
                    iniParser = new PicasaIniParser(path);
                    iniParser.parse();
                }
            }
            
            File folder = new File(path);
            
            List<File> filesToBackup = Arrays.asList(folder.listFiles(app.getFilesToBackupFilter()));
            File picasaIni = PicasaIniParser.picasaIniFor(path);
            if (managedByPicasa) {
                // add picasa.ini to the list of files to copy
                filesToBackup = new ArrayList<File>(filesToBackup);
                filesToBackup.add(picasaIni);
            }
            
            for (File srcFile : filesToBackup) {
                if (tracker.isCancelled()) return null;
                String dest = Files.concatPathParts(destFolder, srcFile.getName());
                logger.info("Copying '" + srcFile.getPath() + "' to '" + dest + "'");

                boolean doCopy = true;
                File destFile = new File(dest);
                if (destFile.exists()) {
                    logger.info("Destination file '" + dest + "' exists");
                    if (overwriteOlder  &&  srcFile.lastModified() > destFile.lastModified()) {
                        logger.info("Overwriting '" + destFile.getPath() + "' with a newer '" + dest + "'");
                        numOverwritten++;
                    } else {
                        String cause;
                        if (overwriteOlder)
                            cause = "Source file is not older than destination";
                        else
                            cause = "Overwrite mode off";
                        logger.info("Won't overwrite the destination file '" + destFile.getPath() + "': " + cause);
                        doCopy = false;
                        numNotOverwritten++;
                    }
                }
                
                if (doCopy) {
                    File parent = destFile.getParentFile();
                    if (!parent.exists()) {
                        parent.mkdirs();
                    }
                    tracker.startTask("Copying " + srcFile.getName(), 0.95 * srcFile.length() / totalBackupSize);
                    Files.copy(srcFile, destFile, tracker);
                    if (tracker.isCancelled()) return null;
                    numCopied++;
                    sizeCopied += destFile.length();
                } else {
                    tracker.startTask("Skipping " + srcFile.getName(), 0.95 * srcFile.length() / totalBackupSize);
                    tracker.taskCompleted();
                }

                if (tracker.isCancelled()) return null;

                if (removeNonStarred  &&  !srcFile.equals(picasaIni)) {
                    if (iniParser == null) {
                        logger.warn("Won't remove '" + srcFile.getPath() + "'. Folder is not managed by Picasa");
                    } else {
                        if (!iniParser.isStarred(srcFile.getName())) {
                            logger.info("Will remove non-starred '" + srcFile.getPath() + "'");
                            nonStarredToRemove.add(srcFile);
                        }
                    }
                }
                
//                tracker.incTaskProgress(100.0 * srcFile.length() / totalBackupSize);
            }
        }
        
        if (removeNonStarred  &&  nonStarredToRemove.size() > 0) {
            tracker.startTask("Removing non-starred", 0.05);
            for (File file : nonStarredToRemove) {
                if (tracker.isCancelled()) return null;
                logger.info("Removing non-starred '" + file.getPath() + "'");
                long fileSize = file.length();
                file.delete();
                numRemoved++;
                sizeRemoved += fileSize;
           }
           if (tracker.isCancelled()) return null;
           tracker.taskCompleted();
        }

        tracker.processFinished();
        logger.info("Backup finished");

        return null;
    }

}
